﻿/* AS3 Version Copyright (C) 2012 Bonnot Guillaume.			    */
/* AS3 porting from a C-program for MT19937, originaly coded by */
/* Takuji Nishimura, considering the suggestions by             */
/* Topher Cooper and Marc Rieffel in July-Aug. 1997.            */
/* This library is free software under the Artistic license:    */
/*                                                              */
/* You can find the original C-program at                       */
/*     http://www.math.keio.ac.jp/~matumoto/mt.html             */
/*                                                              */
/* You can find the reworked C#-program at                      */
/*     http://takel.jp/mt/MersenneTwister.cs	                */
/*                                                              */
/* Porting to AS3 notes :										*/
/*     replace short by uint                                    */
/*     bitwise operator >> makes appear 1 instead of 0 			*/
/*                                                              */

package Utils
{
	public class MersenneTwister
	{
		/* Period parameters */
		private static const N:int = 624;
		private static const M:int = 397;
		private static const MATRIX_A:uint   = 0x9908b0df; /* constant vector a */
		private static const UPPER_MASK:uint = 0x80000000; /* most significant w-r bits */
		private static const LOWER_MASK:uint = 0x7fffffff; /* least significant r bits */

		/* Tempering parameters */
		private static const TEMPERING_MASK_B:uint = 0x9d2c5680;
		private static const TEMPERING_MASK_C:uint = 0xefc60000;
		// Add mask for AS3
		private static function TEMPERING_SHIFT_U(y:uint):uint { return ((y >> 11) & 0x001FFFFF); }
		private static function TEMPERING_SHIFT_S(y:uint):uint { return (y <<  7); }
		private static function TEMPERING_SHIFT_T(y:uint):uint { return (y << 15); }
		private static function TEMPERING_SHIFT_L(y:uint):uint { return ((y >> 18) & 0x00003FFF); }
		// Porting to AS3
		private static function TEMPERING_SHIFT_SIGN(y:uint):uint { return ((y >> 1) & LOWER_MASK); }

		private var mt:Vector.<uint> = new Vector.<uint>(N); /* the array for the state vector  */

		private var mti:uint;

		private static var mag01:Vector.<uint> = new <uint>[ 0x0, MATRIX_A ];

		public function MersenneTwister():void
		{
		}

		/* initializing the array with a NONZERO seed */
		public function reset(seed:uint):void {
			/* setting initial seeds to mt[N] using         */
			/* the generator Line 25 of Table 1 in          */
			/* [KNUTH 1981, The Art of Computer Programming */
			/*    Vol. 2 (2nd Ed.), pp102]                  */
			// mt[0] = seed & 0xffffffffU;
			mt[0] = seed & 0xffffffff;
			for (mti = 1; mti < N; ++mti)
			{
				mt[mti] = (69069 * mt[mti - 1]) & 0xffffffff;
			}
			
		}

		protected function GenerateUInt():uint
		{
			var y:uint;

			/* mag01[x] = x * MATRIX_A  for x=0,1 */
			if (mti >= N) /* generate N words at one time */
			{
				var kk:uint = 0;

				for (; kk < N - M; ++kk)
				{
					y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
					mt[kk] = mt[kk + M] ^ TEMPERING_SHIFT_SIGN(y) ^ mag01[y & 0x1];
				}

				for(;kk < N - 1; ++kk)
				{
					y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
					mt[kk] = mt[kk+(M - N)] ^ TEMPERING_SHIFT_SIGN(y) ^ mag01[y & 0x1];
				}

				y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
				mt[N - 1] = mt[M - 1] ^ TEMPERING_SHIFT_SIGN(y) ^ mag01[y & 0x1];

				mti = 0;
			}

			y = mt[mti++];
			y ^= TEMPERING_SHIFT_U(y);
			y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
			y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
			y ^= TEMPERING_SHIFT_L(y);

			return y;
		}

		public function NextUInt(minValue:uint, maxValue:uint):uint /* throws ArgumentOutOfRangeException */
		{
			if (minValue >= maxValue)
			{
				throw new Error("minValue >= maxValue");
			}

			var diff:uint = maxValue - minValue;
			var ratio:Number = Number(uint.MAX_VALUE) / Number(diff);
			return (uint)( Number(this.GenerateUInt()) / ratio + minValue);
		}

	}
}